Java Proxy

作者 chris.Yun 日期 2018-08-18
Java Proxy

代理模式

代理(Proxy)是一种设计模式,通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
代理模式分为静态代理、动态代理。

静态代理

定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
典型实现方式:

IUserDAO --接口
UserDAOImpl implements IUserDAO --被代理类
UserDAOProxy implements IUserDAO --代理类

缺点:

  1. 必须基于接口实现
  2. 一旦接口增加方法,目标对象及代理对象都需要维护

AspectJ

  1. 静态代理唯一的缺点就是我们需要对每一个方法编写我们的代理逻辑,造成了工作的繁琐和复杂。AspectJ就是为了解决这个问题,在编译成class字节码的时候在方法周围加上业务逻辑。复杂的工作由特定的编译器帮我们做。
  2. Aspectj不受类的特殊限制,不管方法是private、static、或者final,都可以代理。
  3. Aspectj不会代理除了限定方法之外任何其他诸如toString(),clone()等方法。

动态代理

JDK Proxy

JDK会帮我们在运行时生成一个代理类,这个代理类实际上就是我们需要代理的接口的实现类。
实现的方法里面会调用InvocationHandler类中的invoke方法,并且同时传入自身被调用的方法的的Method对象和参数列表方便我们编码实现方法的调用。
JDK的动态代理会代理Object类的equals、hashCode、toString三个方法。

InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.getConstructor(new Class[] { 
    InvocationHandler.class }).newInstance(new Object[] { handler });

or more simply:

Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

CGLib

CGLib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。CGLib包的底层是通过使用一个字节码处理框架ASM来转换字节码并生成新的类。
由于是继承方式,如果是static、private、final方法等方法是不能被代理的。
CGLib做了方法访问优化,使用建立方法索引的方式避免了传统Method的方法反射调用。
CGLIB会默认代理Object中finalize,equals,toString,hashCode,clone等方法。比JDK代理多了finalize和clone。

public class CGLibDynamicProxy implements MethodInterceptor {

    private static CGLibDynamicProxy instance = new CGLibDynamicProxy();

    private CGLibDynamicProxy() {
    }

    public static CGLibDynamicProxy getInstance() {
        return instance;
    }

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> cls) {
        return (T) Enhancer.create(cls, this);
    }

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }
}

Spring AOP

Spring代理实际上是对JDK代理和CGLIB代理做了一层封装,并且引入了AOP概念:Aspect、advice、joinpoint等等,同时引入了AspectJ中的一些注解@pointCut,@after,@before等等.Spring Aop严格的来说都是动态代理,所以实际上Spring代理和Aspectj的关系并不大。

public static void main(String[] args) {
    ProxyFactory proxyFactory = new ProxyFactory();     // 创建代理工厂
    proxyFactory.setTarget(new GreetingImpl());         // 射入目标类对象
    proxyFactory.addAdvice(new GreetingBeforeAdvice()); // 添加前置增强
    proxyFactory.addAdvice(new GreetingAfterAdvice());  // 添加后置增强 

    Greeting greeting = (Greeting) proxyFactory.getProxy(); // 从代理工厂中获取代理
    greeting.sayHello("Jack");                              // 调用代理的方法
}